#region Copyright Notice
// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 
#endregion
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace AbstractClass.Collections.Generic
{
    /// <summary>
    ///   Represents a serializable generic collection of keys and values.
    /// </summary>
    /// <typeparam name = "TKey">The type of the key.</typeparam>
    /// <typeparam name = "TValue">The type of the value.</typeparam>
    [ComVisible(false)]
    [DebuggerDisplay("Count = {Count}")]
    [Serializable]
    public class HashMap<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
    {
        /// <summary>
        ///   Initializes a new instance of the <see cref = "HashMap&lt;TKey,TValue&gt;" /> class.
        /// </summary>
        public HashMap()
        {
        }

        /// <summary>
        ///   Initializes a new instance of the <see cref = "HashMap&lt;TKey,TValue&gt;" /> class.
        /// </summary>
        /// <param name = "capacity">The initial capacity of the collection.</param>
        public HashMap(int capacity) : base(capacity)
        {
        }

        /// <summary>
        ///   Initializes a new instance of the <see cref = "HashMap&lt;TKey,TValue&gt;" /> class.
        /// </summary>
        /// <param name = "comparer">The <see cref="IEqualityComparer&lt;TKey&gt;" /> implementation to use
        /// when comparing keys, or Nothing to use the default <see cref="IEqualityComparer&lt;TKey&gt;" /> for the type of the key.
        /// </param>
        public HashMap(IEqualityComparer<TKey> comparer) : base(comparer)
        {
        }

        /// <summary>
        ///   Initializes a new instance of the <see cref = "HashMap&lt;TKey,TValue&gt;" /> class.
        /// </summary>
        /// <param name = "capacity">The initial capacity of the collection.</param>
        /// <param name = "comparer">The <see cref="IEqualityComparer&lt;TKey&gt;" /> implementation to use
        /// when comparing keys, or Nothing to use the default <see cref="IEqualityComparer&lt;TKey&gt;" /> for the type of the key.
        /// </param>
        public HashMap(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer)
        {
        }

        /// <summary>
        ///   Initializes a new instance of the <see cref = "HashMap&lt;TKey,TValue&gt;" /> class.
        /// </summary>
        /// <param name = "dictionary">The <see cref="IDictionary&lt;TKey,TValue&gt;"/> whose elements are copied
        /// to the new <see cref = "HashMap&lt;TKey,TValue&gt;" />.
        /// </param>
        public HashMap(IDictionary<TKey, TValue> dictionary) : base(dictionary)
        {
        }

        /// <summary>
        ///   Initializes a new instance of the <see cref = "HashMap&lt;TKey,TValue&gt;" /> class.
        /// </summary>
        /// <param name = "dictionary">The <see cref="IDictionary&lt;TKey,TValue&gt;"/> whose elements are copied
        /// to the new <see cref = "HashMap&lt;TKey,TValue&gt;" />.
        /// </param>
        /// <param name = "comparer">The <see cref="IEqualityComparer&lt;TKey&gt;" /> implementation to use
        /// when comparing keys, or Nothing to use the default <see cref="IEqualityComparer&lt;TKey&gt;" /> for the type of the key.
        /// </param>
        public HashMap(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
            : base(dictionary, comparer)
        {
        }

        /// <summary>
        ///   Initializes a new instance of the <see cref = "HashMap&lt;TKey,TValue&gt;" /> class.
        /// </summary>
        /// <param name = "info">The <see cref = "T:System.Runtime.Serialization.SerializationInfo" /> to populate with data.
        /// </param>
        /// <param name = "context">The destination (see <see cref = "T:System.Runtime.Serialization.StreamingContext" />) for this serialization.
        /// </param>
        protected HashMap(SerializationInfo info, StreamingContext context) : base(info, context)
        {
        }

        #region IXmlSerializable Members

        /// <summary>
        ///   This method is reserved and should not be used. When implementing the IXmlSerializable interface, you should return null (Nothing in Visual Basic) from this method, and instead, if specifying a custom schema is required, apply the <see cref = "T:System.Xml.Serialization.XmlSchemaProviderAttribute" /> to the class.
        /// </summary>
        /// <returns>
        ///   An <see cref = "T:System.Xml.Schema.XmlSchema" /> that describes the XML representation of the object that is produced by the <see cref = "M:System.Xml.Serialization.IXmlSerializable.WriteXml(System.Xml.XmlWriter)" /> method and consumed by the <see cref = "M:System.Xml.Serialization.IXmlSerializable.ReadXml(System.Xml.XmlReader)" /> method.
        /// </returns>
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }

        /// <summary>
        ///   Generates a new <see cref = "HashMap&lt;TKey,TValue&gt;" /> from its XML representation.
        /// </summary>
        /// <param name = "reader">The <see cref = "T:System.Xml.XmlReader" /> stream from which the object is deserialized.</param>
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            var keySerializer = new XmlSerializer(typeof (TKey));
            var valueSerializer = new XmlSerializer(typeof (TValue));

            bool wasEmpty = reader.IsEmptyElement;
            reader.Read();

            if (wasEmpty)
                return;

            while (reader.NodeType != XmlNodeType.EndElement)
            {
                reader.ReadStartElement("item");
                reader.ReadStartElement("key");

                var key = (TKey) keySerializer.Deserialize(reader);
                reader.ReadEndElement();

                reader.ReadStartElement("value");
                var value = (TValue) valueSerializer.Deserialize(reader);
                reader.ReadEndElement();
                Add(key, value);
                reader.ReadEndElement();
                reader.MoveToContent();
            }
            reader.ReadEndElement();
        }

        /// <summary>
        ///   Converts a <see cref = "HashMap&lt;TKey,TValue&gt;" /> object into its XML representation.
        /// </summary>
        /// <param name = "writer">The <see cref = "T:System.Xml.XmlWriter" /> stream to which the object is serialized.</param>
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            var keySerializer = new XmlSerializer(typeof (TKey));
            var valueSerializer = new XmlSerializer(typeof (TValue));

            foreach (TKey key in Keys)
            {
                writer.WriteStartElement("item");
                writer.WriteStartElement("key");
                keySerializer.Serialize(writer, key);
                writer.WriteEndElement();
                writer.WriteStartElement("value");
                TValue value = this[key];
                valueSerializer.Serialize(writer, value);
                writer.WriteEndElement();
                writer.WriteEndElement();
            }
        }

        #endregion
    }
}